#!/usr/bin/python
# -*- coding: UTF-8 -*-    
# Author:Jruing
# FileName:DingTalkApi
# DateTime:2021/7/18 11:08
# SoftWare: PyCharm

import requests
import time
import hmac
import hashlib
import urllib
import base64
import traceback
from typing import List

recive = List[str]


class DingTalkApi():
    """
    Document :https://developers.dingtalk.com/document/app/custom-robot-access
     _____   _   __   _   _____   _____       ___   _       _   _
    |  _  \ | | |  \ | | /  ___| |_   _|     /   | | |     | | / /
    | | | | | | |   \| | | |       | |      / /| | | |     | |/ /
    | | | | | | | |\   | | |  _    | |     / / | | | |     | |\ \
    | |_| | | | | | \  | | |_| |   | |    / /  | | | |___  | | \ \
    |_____/ |_| |_|  \_| \_____/   |_|   /_/   |_| |_____| |_|  \_\
    """

    def __init__(self, access_token: str, secret: str = None):
        self.webhook = f"https://oapi.dingtalk.com/robot/send?access_token={access_token}"
        if secret:
            timestamp, sign = self._signature(secret)
            self.webhook = f"{self.webhook}&timestamp={timestamp}&sign={sign}"

    def __str__(self):
        return f"webhook is {self.webhook}"

    @staticmethod
    def _signature(secret):
        timestamp = str(round(time.time() * 1000))
        secret_enc = secret.encode('utf-8')
        string_to_sign = f'{timestamp}\n{secret}'
        string_to_sign_enc = string_to_sign.encode('utf-8')
        hmac_code = hmac.new(secret_enc, string_to_sign_enc, digestmod=hashlib.sha256).digest()
        sign = urllib.parse.quote_plus(base64.b64encode(hmac_code))
        return timestamp, sign

    def client_conn(self, data):
        try:
            response = requests.post(self.webhook, json=data).json()
            return response
        except Exception:
            raise Exception(traceback.format_exc())

    def send_text_msg(self, content: str, atMobiles: list = None, atUserIds: list = None,
                      isAtAll: bool = False) -> dict:
        """
        发送文本类型信息

        :param msgtype:消息类型，此时固定为：text。
        :param content:	消息内容。
        :param atMobiles:被@人的手机号。注意 在content里添加@人的手机号。
        :param atUserIds:被@人的用户userid。
        :param isAtAll:是否@所有人。默认位False
        :return:

        example:
        {
            "at": {
                "atMobiles":[
                    "13888888888"
                ],
                "atUserIds":[
                    "user123"
                ],
                "isAtAll": false
            },
            "text": {
                "content":"我就是我, @13888888888 是不一样的烟火"
            },
            "msgtype":"text"
        }
        """
        atMobiles = atMobiles if isinstance(atMobiles, list) and atMobiles else []
        atUserIds = atUserIds if isinstance(atUserIds, list) and atUserIds else []

        params = {
            "at": {
                "atMobiles": atMobiles,
                "atUserIds": atUserIds,
                "isAtAll": isAtAll
            },
            "text": {
                "content": content
            },
            "msgtype": "text"
        }
        return self.client_conn(params)

    def send_link_msg(self, title: str, text: str, messageUrl: str, picUrl: str = "") -> dict:
        """
        发送链接类型消息

        :param title: 消息标题
        :param text: 消息内容,如果太长只会部分展示
        :param messageUrl: 点击消息跳转的url
        :param picUrl: 图片Url
        :return:

        example:
        {
            "msgtype": "link",
            "link": {
                "text": "这个即将发布的新版本，创始人xx称它为红树林。而在此之前，每当面临重大升级，产品经理们都会取一个应景的代号，这一次，为什么是红树林",
                "title": "时代的火车向前开",
                "picUrl": "",
                "messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI"
                }
        }
        """
        if title and text and messageUrl:
            params = {
                "msgtype": "link",
                "link": {
                    "text": text,
                    "title": title,
                    "picUrl": picUrl,
                    "messageUrl": messageUrl
                }
            }
            return self.client_conn(params)
        else:
            raise Exception("title | text | messageUrl 不能为空")

    def send_feedcard_msg(self, title: list, messageURL: list, picURL: list) -> dict:
        """
        发送feedcard类型消息

        :param title:单条信息文本 ["时代的火车向前开1",时代的火车向前开2]
        :param messageURL:点击单条信息到跳转链接 ["https://www.dingtalk.com/","https://www.dingtalk.com/"]
        :param picURL:单条信息后面图片的URL ["https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png","https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"]
        :return:

        example:
        {
            "msgtype":"feedCard",
            "feedCard": {
                "links": [
                   {
                        "title": "时代的火车向前开1",
                        "messageURL": "https://www.dingtalk.com/",
                        "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"
                    },
                    {
                        "title": "时代的火车向前开2",
                        "messageURL": "https://www.dingtalk.com/",
                        "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png"
                    }
                ]
            }
        }
        """

        if title and messageURL and picURL:
            params = {
                "msgtype": "feedCard",
                "feedCard": {
                    "links": []
                }
            }
            for t, m, p in zip(title, messageURL, picURL):
                params["feedCard"]['links'].append({"title": t, "messageURL": m, "picURL": p})
            return self.client_conn(params)
        else:
            raise Exception("title | messageURL | picURL 不能为空")

    def send_markdwon_msg(self, title: str, text: str, atMobiles: list = None, atUserIds: list = None,
                          isAtAll: bool = False) -> dict:
        """
        发送markdown类型消息

        :param title: 首屏会话透出的展示内容。
        :param text:markdown格式的消息。
        :param atMobiles:被@人的手机号。
        :param atUserIds:被@人的用户userid。
        :param isAtAll:是否@所有人。
        :return:

        example:
        {
            "msgtype": "markdown",
            "markdown": {
                "title":"杭州天气",
                "text": "#### 杭州天气 @150XXXXXXXX \n > 9度，西北风1级，空气良89，相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n"
            },
            "at": {
                "atMobiles": [
                    "150XXXXXXXX"
                ],
                "atUserIds": [
                    "user123"
                ],
                "isAtAll": false
            }
        }
        """
        atMobiles = atMobiles if isinstance(atMobiles, list) and atMobiles else []
        atUserIds = atUserIds if isinstance(atUserIds, list) and atUserIds else []
        params = {
            "msgtype": "markdown",
            "markdown": {
                "title": title,
                "text": text
            },
            "at": {
                "atMobiles": atMobiles,
                "atUserIds": atUserIds,
                "isAtAll": isAtAll
            }
        }
        return self.client_conn(params)

    def send_actioncard_msg(self, title: str, text: str, title_child: list, actionURL: list,
                            btnOrientation: str = "0") -> dict:
        """
        发送整体卡片

        :param title: 首屏会话透出的展示内容。
        :param text:markdown格式的消息。
        :param btns:按钮
        :param title_child:按钮标题
        :param actionURL:点击按钮触发的URL
        :param btnOrientation:0：按钮竖直排列  1：按钮横向排列 默认为0
        :return:

        example:
        {
            "msgtype": "actionCard",
            "actionCard": {
                "title": "我 20 年前想打造一间苹果咖啡厅，而它正是 Apple Store 的前身",
                "text": "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化，而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划",
                "btnOrientation": "0",
                "btns": [
                    {
                        "title": "内容不错",
                        "actionURL": "https://www.dingtalk.com/"
                    },
                    {
                        "title": "不感兴趣",
                        "actionURL": "https://www.dingtalk.com/"
                    }
                ]
            }
        }
        """
        params = {
            "msgtype": "actionCard",
            "actionCard": {
                "title": title,
                "text": text,
                "btnOrientation": btnOrientation,
                "btns": []
            }
        }
        for t, a in zip(title_child, actionURL):
            params["actionCard"]['btns'].append({"title": t, "actionURL": a})
        return self.client_conn(params)

    def send_whole_actioncard_msg(self, title: str, text: str, singleTitle: str, singleURL: str,
                                  btnOrientation: str = "0") -> dict:
        """
        发送独立卡片

        :param title:首屏会话透出的展示内容。
        :param text:markdown格式的消息。
        :param singleTitle:单个按钮的标题。注意 设置此项和singleURL后，btns无效。
        :param singleURL:点击singleTitle按钮触发的URL。
        :param btnOrientation:0：按钮竖直排列  1：按钮横向排列 默认为0
        :return:

        example:
        {
            "actionCard": {
                "title": "乔布斯 20 年前想打造一间苹果咖啡厅，而它正是 Apple Store 的前身",
                "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png) \n### 乔布斯 20 年前想打造的苹果咖啡厅Apple Store 的设计正从原来满满的科技感走向生活化，而其生活化的走向其实可以追溯到20        年前苹果一个建立咖啡馆的计划        ",
                "btnOrientation": "0",
                "singleTitle": "阅读全文",
                "singleURL": "https://www.dingtalk.com/"
            },
            "msgtype": "actionCard"
        }
        """
        params = {
            "actionCard": {
                "title": title,
                "text": text,
                "btnOrientation": btnOrientation,
                "singleTitle": singleTitle,
                "singleURL": singleURL
            },
            "msgtype": "actionCard"
        }
        return self.client_conn(params)

    def send_msg(self, send_type, **kwargs):
        try:
            _models = ["send_text_msg", "send_link_msg", "send_feedcard_msg", "send_markdwon_msg",
                       "send_actioncard_msg", "send_whole_actioncard_msg"]
            getattr(self, send_type)(**kwargs)
        except Exception:
            raise Exception(traceback.format_exc())


if __name__ == '__main__':
    dd = DingTalkApi('<access_token>', '<access_token>')
    dd.send_msg('__str__')
    dd.send_msg('send_text_msg', content="hello world@13888888888", atMobiles=['13888888888'])
    # dd.send_text_msg("hello world@13888888888",atMobiles=['13888888888'])
    # dd.send_link_msg("百度一下", "", "http://www.baidu.com",
    #                  "https://static-aliyun-doc.oss-accelerate.aliyuncs.com/assets/img/zh-CN/4099076061/p131227.png")
    # dd.send_feedcard_msg(title=["hello world"],messageURL=["https://www.baidu.com"],picURL=["https://static-aliyun-doc.oss-accelerate.aliyuncs.com/assets/img/zh-CN/4099076061/p131227.png"])
    # dd.send_markdwon_msg("hello", "# hello \n### world")
    # dd.send_actioncard_msg("hello", "# kaishi \n aaaaa", ["还行"], ["https://www.dingtalk.com/"])
    # dd.send_whole_actioncard_msg("hello", "# hello", "点击一下",
    #                              "https://static-aliyun-doc.oss-accelerate.aliyuncs.com/assets/img/zh-CN/5099076061/p131217.png")
